package de.jmda.core.mproc.task;

import static de.jmda.core.mproc.ProcessingUtilities.asDeclaredType;
import static de.jmda.core.mproc.ProcessingUtilities.asTypeElement;
import static de.jmda.core.mproc.ProcessingUtilities.asVariableElement;
import static de.jmda.core.mproc.ProcessingUtilities.isProcessingOver;
import static de.jmda.core.util.CollectionsUtil.asSet;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.io.IOException;
import java.util.Set;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

import org.junit.BeforeClass;
import org.junit.Test;

import de.jmda.core.mproc.task.AbstractTypeElementsTaskTypes;
import de.jmda.core.mproc.task.TaskException;
import de.jmda.core.mproc.task.TaskRunner;

public class JUTNavigateElementsAggregation
{
//	private final static Logger LOGGER =
//			Logger.getLogger(JUTNavigateElementsAggregation.class);

	@SuppressWarnings("unused")
  private class A
	{
		private B b;
		private int i;
	}

	private class B { }
	
	private static class TypeElementsTask extends AbstractTypeElementsTaskTypes
	{
		private static TypeElement typeelement_A;
		private static Element element_A_b;
		private static Element element_A_i;
		private static VariableElement variableelement_A_b;
		private static VariableElement variableelement_A_i;
		private static TypeMirror typemirror_A_b;
		private static TypeMirror typemirror_A_i;
		private static DeclaredType declaredtype_A_b;
		private static DeclaredType declaredtype_A_i;
		private static Element element_B;
		private static TypeElement typeelement_B;

		public TypeElementsTask(Set<? extends Class<?>> types)
    {
	    super(types);
    }

    @Override
		public boolean execute() throws TaskException
		{
    	if (false == isProcessingOver())
			{
				// get a type element representation for type A
				typeelement_A = getTypeElements().iterator().next();

				// navigate to A's elements
				element_A_b = findElement(typeelement_A.getEnclosedElements(), "b");
				element_A_i = findElement(typeelement_A.getEnclosedElements(), "i");

				// get A's elements as variable elements
				variableelement_A_b = asVariableElement(element_A_b);
				variableelement_A_i = asVariableElement(element_A_i);

				// get the types of A's elements as type mirrors
				typemirror_A_b = element_A_b.asType();
				typemirror_A_i = element_A_i.asType();

				// get the types of A's elements as declared types (null for field i)
				declaredtype_A_b = asDeclaredType(typemirror_A_b);
				declaredtype_A_i = asDeclaredType(typemirror_A_i);

				// get the type of A's b element as element
				element_B = declaredtype_A_b.asElement();

				// get the type element of A's b element as type element
				typeelement_B = asTypeElement(element_B);
			}

			return false;
		}

		private Element findElement(Iterable< ? extends Element> elements, String name)
		{
			for (Element element : elements)
			{
				if (element.getSimpleName().toString().equals(name))
				{
					return element;
				}
			}

			return null;
		}
	}

	@BeforeClass
	public static void beforeClass() throws IOException
	{
		TypeElementsTask task = new TypeElementsTask(asSet(A.class));

		TaskRunner.run(task);

//		LOGGER.debug("typeelement_A: " + TypeElementsTask.typeelement_A);
//		LOGGER.debug("element_A_b: " + TypeElementsTask.element_A_b);
//		LOGGER.debug("element_A_i: " + TypeElementsTask.element_A_i);
//		LOGGER.debug("variableelement_A_b: " + TypeElementsTask.variableelement_A_b);
//		LOGGER.debug("variableelement_A_i: " + TypeElementsTask.variableelement_A_i);
//		LOGGER.debug("typemirror_A_b: " + TypeElementsTask.typemirror_A_b);
//		LOGGER.debug("typemirror_A_i: " + TypeElementsTask.typemirror_A_i);
//		LOGGER.debug("declaredtype_A_b: " + TypeElementsTask.declaredtype_A_b);
//		LOGGER.debug("declaredtype_A_i: " + TypeElementsTask.declaredtype_A_i); // null
//		LOGGER.debug("element_B: " + TypeElementsTask.element_B);
//		LOGGER.debug("typeelement_B: " + TypeElementsTask.typeelement_B);
	}

	@Test
	public void testTypeElementA() throws IOException
	{
		assertNotNull("typeelement_A is null", TypeElementsTask.typeelement_A);
	}

	@Test
	public void testTypeElementB() throws IOException
	{
		assertNotNull("typeelement_B is null", TypeElementsTask.typeelement_B);
	}

	@Test
	public void testElement_A_b() throws IOException
	{
		assertNotNull("element_A_b is null", TypeElementsTask.element_A_b);
	}

	@Test
	public void testElement_A_i() throws IOException
	{
		assertNotNull("element_A_i is null", TypeElementsTask.element_A_i);
	}

	@Test
	public void testVariableElement_A_b() throws IOException
	{
		assertNotNull("variableelement_A_b is null", TypeElementsTask.variableelement_A_b);
	}

	@Test
	public void testVariableElement_A_i() throws IOException
	{
		assertNotNull("variableelement_A_i is null", TypeElementsTask.variableelement_A_i);
	}

	@Test
	public void testTypeMirror_A_b() throws IOException
	{
		assertNotNull("typemirror_A_b is null", TypeElementsTask.typemirror_A_b);
	}

	@Test
	public void testTypeMirror_A_i() throws IOException
	{
		assertNotNull("typemirror_A_i is null", TypeElementsTask.typemirror_A_i);
	}

	@Test
	public void testDeclaredType_A_b() throws IOException
	{
		assertNotNull("declaredtype_A_b is null", TypeElementsTask.declaredtype_A_b);
	}

	@Test
	public void testDeclaredType_A_i() throws IOException
	{
		assertNull("declaredtype_A_i is not null", TypeElementsTask.declaredtype_A_i);
	}

	@Test
	public void testElement_B() throws IOException
	{
		assertNotNull("element_B is null", TypeElementsTask.element_B);
	}
}